package concurrent.c_012_ThreadPool;

import lombok.AllArgsConstructor;
import util.TimeUtil;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/**
 * 如果你向 Executor 提交了一个批处理任务，并且希望在它们完成后获得结果，怎么办呢？
 * Java8 之前的做法是让返回 Futrue，然后调用其 get 阻塞方法即可。Java8 提供了一个更好的方法：完成服务 (CompletionService)。
 * CompletionService 整合了 Executor 和 BlockingQueue 的功能。你可以将 Callable 任务提交给它去执行，
 * 然后使用类似于队列中的 take 和 poll 方法，在结果完整可用时获得这个结果，像一个使用 BlockingQueue 打包的 Future。
 * CompletionService 是 Java8 的新增接口，JDK 为其提供了一个实现类 ExecutorCompletionService。
 * 原理：内部通过阻塞队列 + FutureTask，实现了任务先完成可优先获取到，即结果按照完成先后顺序排序。
 */
public class T08_CompletionService {
    // 开启 10 个线程
    static ExecutorService exs = Executors.newFixedThreadPool(10);

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        // 结果集
        List<Integer> list = new ArrayList<>();
        List<Future<Integer>> futureList = new ArrayList<>();
        try {
            int taskCount = 10;
            // 1.定义 CompletionService，ExecutorCompletionService 是此接口的唯一实现类，需要把线程池传进去
            CompletionService<Integer> completionService = new ExecutorCompletionService<>(exs);
            // 2.添加任务（向 CompletionService 添加任务，然后把返回的 futrue 添加到 futureList 即可）
            for (int i = 0; i < taskCount; i++) {
                futureList.add(completionService.submit(new Task(i + 1)));
            }

            // ================== 结果归集 ===================

            // 方法 1：future 是提交时返回的，遍历 futureList 则按照任务提交顺序获取结果(若是按照提交顺序，那和 T06_Futrue 的 Demo 结果将一样，没啥优势可言)
            // for (Future<Integer> future : futureList) {
            //     System.out.println("====================");
            //     Integer result = future.get(); // 线程在这里阻塞等待该任务执行完毕,按照
            //     System.out.println("任务 result = " + result + " 获取到结果！" + TimeUtil.nowDateTime());
            //     list.add(result);
            // }

            // 方法 2.使用内部阻塞队列的 take()：内部维护阻塞队列，任务先完成的先获取到
            for (int i = 0; i < taskCount; i++) {
                Integer result = completionService.take().get(); // 采用 completionService.take()，
                System.out.println("任务 i = " + result + " 完成！" + TimeUtil.nowDateTime());
                list.add(result);
            }

            System.out.println("按照线程执行完成顺序排序，任务处理结果 list = " + list);
            System.out.println("总耗时 = " + (System.currentTimeMillis() - start));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            exs.shutdown(); // 关闭线程池
        }
    }

    @AllArgsConstructor
    static class Task implements Callable<Integer> {
        Integer i;

        @Override
        public Integer call() throws Exception {
            if (i == 5) {
                Thread.sleep(5000);
            } else {
                Thread.sleep(1000);
            }
            System.out.println("线程：" + Thread.currentThread().getName() + " 任务 i = " + i + "，执行完成！");
            return i;
        }
    }
}
